#include "Program.h"

#include <compilation/task/PhiInsertionTask.h>

#include <compilation/task/PhiFrontierTask.h>


namespace compilation
{


void
Program::insertPhiFunctions(void)
{
  UNSET_DEBUG_FOR_VISITORS();

  if (debug_instruction_visitor)
    std::cout << "insertPhiFunction:\n"
                 "------------------\n";

  PhiInsertionAgenda::instruction_handler_visitor_type piihv =
      PhiInsertionAgenda::instruction_handler_visitor_type
      ::build_visitor<PhiInsertionInstructionHandler>();

  PhiInsertionAgenda::expression_handler_visitor_type piehv =
      PhiInsertionAgenda::expression_handler_visitor_type
      ::build_visitor<PhiInsertionExpressionHandler>();

  PhiInsertionAgenda::instruction_propagator_visitor_type piipv =
      PhiInsertionAgenda::instruction_propagator_visitor_type
      ::build_visitor<PhiInsertionPropagator>();

  LabelPhiFrontierAgenda::instruction_handler_visitor_type lpfihv =
      LabelPhiFrontierAgenda::instruction_handler_visitor_type
      ::build_visitor<LabelPhiFrontierInstructionHandler>();

  LabelPhiFrontierAgenda::instruction_propagator_visitor_type lpfipv =
      LabelPhiFrontierAgenda::instruction_propagator_visitor_type
      ::build_visitor<DefaultInstructionPropagator
      <LabelPhiFrontierTask, LabelPhiFrontierAgenda>::type>();

  for (Function const& fn : m_functions)
  {
    if (debug_instruction_visitor)
      std::cout << "PhiInsertionAgenda: " << fn.getName() << std::endl
                << "°°°°°°°°°°°°°°°°°°°\n";
    PhiInsertionAgenda piAgenda(piihv, piehv, piipv, fn);
    execute(piAgenda);

    for (auto label = piAgenda.labels().rbegin(); label != piAgenda.labels().rend(); ++label)
    {
      if (debug_instruction_visitor)
        std::cout << "LabelPhiFrontierAgenda: "
                  << (*label)->getRegistrationIndex()
                  << "\n°°°°°°°°°°°°°°°°°°°°°°°\n";
      LabelPhiFrontierAgenda lpfAgenda(lpfihv, lpfipv);
      lpfAgenda.propagate(**label);
      execute(lpfAgenda);
    }

    insertPhiExpressions(const_cast<Function&>(fn), piAgenda.labels());
  }
}


void Program::insertPhiExpressions(
    Function &function,
    const std::vector<LabelInstruction*> &labels)
{
  if (debug_instruction_visitor)
    std::cout << "insertPhiExpression:\n"
                 "--------------------\n";

  for (std::vector<LabelInstruction*>::const_iterator iter =
       labels.begin();
       iter != labels.end();
       ++iter)
  {
    LabelInstruction& label = **iter;
    if (label.mark)
    {
      PhiInsertionTask::LabelResult const& result =
          label.getMarkAs<PhiInsertionTask::LabelResult>();

      VirtualInstruction* previous = &label;
      for (auto labelIter = result.map().begin();
           labelIter != result.map().end(); ++labelIter)
      {
        if (labelIter->second.first || labelIter->second.second)
        {
          ExpressionInstruction* assign = new ExpressionInstruction();
          assign->setScope(*previous->getScope());
          function.insertNewInstructionAfter(assign, *previous);
          previous = assign;

          AssignExpression* assignExpression = new AssignExpression();
          assign->setExpression(assignExpression);

          VirtualExpression* lvalue = labelIter->first->clone();
          assignExpression->setLValue(lvalue);

          PhiExpression* phi = new PhiExpression();
          assignExpression->setRValue(phi);

          if (labelIter->second.first)
            phi->addReference(labelIter->first->clone(), *labelIter->second.first);
          if (labelIter->second.second)
            phi->addReference(labelIter->first->clone(), *labelIter->second.second);
        }
      }
      delete &result;
      label.clearMark();
    }
  }
}


} // compilation
